package xcmg.device.service.safetyStock;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xcmg.device.dao.entity.safetyStock.MaterialsSalesData;
import xcmg.device.dao.mapper.BuyPlanMapper;
import xcmg.device.dao.mapper.safetyStock.MaterialsSalesDataMapper;
import xcmg.device.service.safetyStock.SafetyInventoryStrategy.SafetyInventoryService;
import xcmg.device.service.vo.safetyStock.SafetyLevelInfo;
import xcmg.device.util.DecimalUtil;

/**
 * 安全库存方案一
 *
 * SS = Z * sqrt（交货期 * 近13个月出货量与均需求量之间的方差δ），交货期从sap取出是天，此处要除以30转换为月
 * 近13个月出货量与近12个月均需求量之间的方差δ = 0.9 * 近12个月平均出货量方差 + 0.1 * 当前月与均出货量间的方差
 *
 * 近12个月平均出货量方差 = 当前月前12个月的方差
 * 当前月与均出货量间的方差 = 当前月与前12个月均出货量的方差
 * @author wangyong
 */
@Service("safetyInventoryOptionOneService")
public class SafetyInventoryOptionOneServiceImpl extends SafetyInventoryCommonService implements SafetyInventoryService {


  @Autowired
  private MaterialsSalesDataMapper materialsSalesDataMapper;

  @Autowired
  private BuyPlanMapper buyPlanMapper;

  /**
   * 近13个月出货量与均需求量之间的方差δ = 0.9 * 近12个月平均出货量方差 + 0.1 * 当前月与均出货量间的方差
   *
   * @param materialsSalesData
   * @return
   */
  @Override
  public BigDecimal calSalesVariance(MaterialsSalesData materialsSalesData) {
    BigDecimal nearly12MonthsVariance =  DecimalUtil.variance(getNearly12MonthsArray(materialsSalesData), 4);
    BigDecimal lastVariance =  DecimalUtil.variance(getLastMonthArray(materialsSalesData), 4);

    BigDecimal result = new BigDecimal("0.9").multiply(nearly12MonthsVariance).add(
        new BigDecimal("0.1").multiply(lastVariance));

    return DecimalUtil.extractRoot(result, 4);
  }

  /**
   * 方案一出货频次以月份计算，只要这个月有出货，则计1
   *
   * @param materialsSalesData
   * @return
   */
  private BigDecimal calOutRate(MaterialsSalesData materialsSalesData) {
    BigDecimal[] salesArray = getNearly12MonthsArray(materialsSalesData);
    int count = 0;
    for (BigDecimal salesCount : salesArray) {
      if (salesCount.compareTo(BigDecimal.ZERO) > 0) {
        count++;
      }
    }
    return new BigDecimal(count);
  }

  /**
   * 获取上个月的年月yyyy-MM
   * @param date
   * @return
   */
  private String getLastMonthStr(Date date) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    //月初执行，应该取上个月的时间
    calendar.add(Calendar.MONTH,-1);
    return sdf.format(calendar.getTime());
  }

  @Override
  public SafetyLevelInfo getSafetyInfo(String orgId, String materialsNo, Date now, BigDecimal deliverySpend) throws Exception{

    MaterialsSalesData materialsSalesData = materialsSalesDataMapper.getByMaterialsNo(orgId, materialsNo);

    BigDecimal[] arr = getLastMonthArray(materialsSalesData);
    // 预测需求量=当前月*0.1+前12个月平均值*0.9
    BigDecimal forecastNeed = (new BigDecimal("0.1").multiply(arr[0])).add(new BigDecimal("0.9").multiply(arr[1]));

    // 订货次数AL=零件在当前月主机厂的采购计划中出现的次数，如果无订货次数则默认为1
    Integer orderCount = buyPlanMapper.countMaterialsPlan(materialsNo, orgId, getLastMonthStr(now));
    if(null == orderCount || orderCount.equals(0)) {
      orderCount = 1;
    }

    // 周转库存EOQ=预测需求量D/订货次数AL
    BigDecimal eoqStock = forecastNeed.divide(new BigDecimal(orderCount),0, BigDecimal.ROUND_UP);
    //交货期
    if (null == deliverySpend) {
      deliverySpend = getDeliverySpend(orgId, materialsNo);
    }
    SafetyLevelInfo safetyLevelInfo = new SafetyLevelInfo();
    if (null == deliverySpend) {
      return safetyLevelInfo;
    }
    //周转储备库存LTD=交货期*预测需求量D
    BigDecimal ltdStock = forecastNeed
        .multiply(deliverySpend).setScale(0, BigDecimal.ROUND_UP);

    BigDecimal outRate = calOutRate(materialsSalesData);
    SafeLevelEnum safeLevelEnum = SafeLevelEnum.getByOutRate(outRate);

    safetyLevelInfo.setDeliverySpend(deliverySpend);
    safetyLevelInfo.setOutRate(outRate);
    safetyLevelInfo.setSafetyConstant(safeLevelEnum.getLevelValue());
    safetyLevelInfo.setSafetyLevel(safeLevelEnum.getLevelDesc());
    safetyLevelInfo.setEoqStock(eoqStock);
    safetyLevelInfo.setLtdStock(ltdStock);

    return safetyLevelInfo;
  }

  /**
   * 最近一个月之前12个月的出货量的数组
   *
   * @param materialsSalesData
   * @return
   */
  private BigDecimal[] getNearly12MonthsArray(MaterialsSalesData materialsSalesData) {
    BigDecimal[] salesArray = {materialsSalesData.getLastMonth2(),
        materialsSalesData.getLastMonth3(),
        materialsSalesData.getLastMonth4(),
        materialsSalesData.getLastMonth5(),
        materialsSalesData.getLastMonth6(),
        materialsSalesData.getLastMonth7(),
        materialsSalesData.getLastMonth8(),
        materialsSalesData.getLastMonth9(),
        materialsSalesData.getLastMonth10(),
        materialsSalesData.getLastMonth11(),
        materialsSalesData.getLastMonth12(),
        materialsSalesData.getLastMonth13()};
    return salesArray;
  }


  /**
   * 安全等级信息维护
   */
  private enum SafeLevelEnum {
    LEVEL_G("G", BigDecimal.ZERO, new BigDecimal("2"), new BigDecimal("0.15")),
    LEVEL_F("F", new BigDecimal("3"), new BigDecimal("4"), new BigDecimal("0.2")),
    LEVEL_E("E", new BigDecimal("5"), new BigDecimal("6"), new BigDecimal("0.25")),
    LEVEL_D("D", new BigDecimal("7"), new BigDecimal("7"), new BigDecimal("0.52")),
    LEVEL_C("C", new BigDecimal("8"), new BigDecimal("8"), new BigDecimal("0.84")),
    LEVEL_B("B", new BigDecimal("9"), new BigDecimal("10"), new BigDecimal("1.28")),
    LEVEL_A("A", new BigDecimal("11"), new BigDecimal("12"), new BigDecimal("1.64"));

    /**
     * 安全系数描述
     */
    private String levelDesc;

    /**
     * 该等级最小安全频次值
     */
    private BigDecimal minOutRate;

    /**
     * 该等级最大安全频次值
     */
    private BigDecimal maxOutRate;

    /**
     * 安全系数值
     */
    private BigDecimal levelValue;

    SafeLevelEnum(String levelDesc, BigDecimal minOutRate, BigDecimal maxOutRate,
        BigDecimal levelValue) {
      this.levelDesc = levelDesc;
      this.minOutRate = minOutRate;
      this.maxOutRate = maxOutRate;
      this.levelValue = levelValue;
    }

    public String getLevelDesc() {
      return levelDesc;
    }

    public void setLevelDesc(String levelDesc) {
      this.levelDesc = levelDesc;
    }

    public BigDecimal getMinOutRate() {
      return minOutRate;
    }

    public void setMinOutRate(BigDecimal minOutRate) {
      this.minOutRate = minOutRate;
    }

    public BigDecimal getMaxOutRate() {
      return maxOutRate;
    }

    public void setMaxOutRate(BigDecimal maxOutRate) {
      this.maxOutRate = maxOutRate;
    }

    public BigDecimal getLevelValue() {
      return levelValue;
    }

    public void setLevelValue(BigDecimal levelValue) {
      this.levelValue = levelValue;
    }

    /**
     * 根据出货频次获取安全系数数据
     *
     * @param outRate
     * @return
     */
    static SafeLevelEnum getByOutRate(BigDecimal outRate) {
      for (SafeLevelEnum safeLevelEnum : SafeLevelEnum.values()) {
        if (safeLevelEnum.getMinOutRate().compareTo(outRate) <= 0 && safeLevelEnum.getMaxOutRate().compareTo(outRate) >= 0) {
          return safeLevelEnum;
        }
      }
      return null;
    }
  }
}
